package chess.client;

import java.util.ArrayList;
import java.util.HashMap;

import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Timer;

import chess.client.ChessSquare;

import java.util.Date;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;

public class ChessBoard extends Composite {
	public ChessSquare chessSquares[][];
	public ChessSquare activeChessPiece;
	
	private Grid gridChessMovesHistory;
	public AbsolutePanel chessBoardDragPanel = new AbsolutePanel();
	
	public ArrayList<ChessMove> chessMoves = new ArrayList<ChessMove>();
	
	public ArrayList<ArrayList<Integer[]>> playerPieces = new ArrayList<ArrayList<Integer[]>>();
	
	private HashMap<String, Integer[]> chessPieceUnicode = new HashMap<String, Integer[]>();
	
	public ChessBoardDragController chessBoardDragController;
	public ChessBoardDropController chessBoardDropController;
	
	public Integer nCurrentTurn = 1;
	public Date aCurrentPlayerTimers[];
	public Long aPlayerTimers[];
	public boolean bMakeDraggableRunning = false;
	
	public Timer ChessTimer = new Timer() {
	      @Override
	      public void run() {
				Date currentTime = new Date();
				
				String minutesValue = Long.toString((aPlayerTimers[nCurrentTurn] + (currentTime.getTime() - aCurrentPlayerTimers[nCurrentTurn].getTime())) / (60 * 1000) % 60);
				if (minutesValue.length() < 1)
					minutesValue = "00";
				else if (minutesValue.length() == 1)
					minutesValue = "0" + minutesValue;
				
				String secondsValue = Long.toString((aPlayerTimers[nCurrentTurn] + (currentTime.getTime() - aCurrentPlayerTimers[nCurrentTurn].getTime())) / 1000 % 60);
				if (secondsValue.length() < 1)
					secondsValue = "00";
				else if (secondsValue.length() == 1)
					secondsValue = "0" + secondsValue;
				
				String newTimerValue = minutesValue + ":" + secondsValue;

				((Element) RootPanel.get(Constants.PieceNames[nCurrentTurn] + "Display").getElement().getChild(1).getChild(2).getChild(3).getChild(1).getChild(1).getChild(0).getChild(5)).setInnerText(newTimerValue);
			}
	    };
	
	public Integer findPlayerPiece(Integer nPlayer, Integer row, Integer col) {
		Integer[] currentPiece;
		
		for (int i = 0; i < playerPieces.get(nPlayer).size(); i++) {
			currentPiece = playerPieces.get(nPlayer).get(i);
			
			if (row == currentPiece[0] && col == currentPiece[1]) {
				return i;	
			}
		}
		
		return -1;
	}
	
	public ChessBoard() {
		Grid gridChessBoard = new Grid(2, 2);
		Grid gridChessMoves = new Grid(8, 8);
		chessSquares = new ChessSquare[8][8];

		String sPiece;
		String[] aPieceColor = {"w", "b"};
		int i;
		
		playerPieces.add(new ArrayList<Integer[]>());
		playerPieces.add(new ArrayList<Integer[]>());
		
		//draw labels
			HTML chessBoardLabel;
			
			//Columns
				Grid gridLabelsCols = new Grid(1, 8);
				for (int col = 0; col < 8; col++) {
					chessBoardLabel = new HTML(String.valueOf( (char) ("a".charAt(0) + col)));
					chessBoardLabel.setStyleName("chessboardlabelcols");
					
					gridLabelsCols.setWidget(0, col, chessBoardLabel);
				}
				gridLabelsCols.setStyleName("chessboardtable");	
				gridChessBoard.setWidget(1, 1, gridLabelsCols);
			
			//Rows
				Grid gridLabelsRows = new Grid(8, 1);
				for (int row = 0; row < 8; row++) {
					chessBoardLabel = new HTML(Integer.toString(8-row));
					chessBoardLabel.setStyleName("chessboardlabelrows");
					
					gridLabelsRows.setWidget(row, 0, chessBoardLabel);
				}
				gridLabelsRows.setStyleName("chessboardtable");
				gridChessBoard.setWidget(0, 0, gridLabelsRows);
		
		//draw pieces				
		for (int col = 0; col < 8; col++) {
			for (int row = 0; row < 8; row++) {
				//Initialize Pieces
					sPiece = null;
					
					for (i = 0; i < 2; i++) {
						if (row == 0+i*7)
						{
							//Rooks
								if (col%7==0)
								{
									sPiece = "r";
									break;
								}
								
							//Knights
								if ((col-1)%5==0)
								{
									sPiece = "n";
									break;
								}
								
							//Bishops
								if ((col-2)%3==0)
								{
									sPiece = "b";
									break;
								}
							//Queen
								if (col==3)
								{
									sPiece = "q";
									break;
								}
							//King
								if (col==4)
								{
									sPiece = "k";
									break;
								}
						}
						//Pawns
							if (row == 1+i*5)
							{
								sPiece = "p";
								break;
							}
					}
					
					if (sPiece != null)
					{
						sPiece = aPieceColor[i] + sPiece;
						
						playerPieces.get(i).add(new Integer[] {row, col});
					}
					
				chessSquares[row][col] = new ChessSquare(row, col, sPiece);
				
				gridChessMoves.setWidget(7-row, col, chessSquares[row][col]);
			}
		}

		gridChessMoves.setStyleName("chessboardtable");
		
		AbsolutePanel aDropPanel = new AbsolutePanel();
	    aDropPanel.add(gridChessMoves);
	    chessBoardDragPanel.add(aDropPanel);

		chessBoardDragController = new ChessBoardDragController(this, chessBoardDragPanel, true);
		chessBoardDropController = new ChessBoardDropController(this, aDropPanel, Constants.ChessSquareSize, Constants.ChessSquareSize);
		
		chessBoardDragController.registerDropController(chessBoardDropController);
		chessBoardDragPanel.setPixelSize(115 * 8, 115 * 8);

		gridChessBoard.setStyleName("chessboardtable");
		
		gridChessBoard.setWidget(0, 1, chessBoardDragPanel);
		initWidget (gridChessBoard);
		
		chessPieceUnicode.put("wk", new Integer[] {0, 0});
		chessPieceUnicode.put("wq", new Integer[] {1, 0});
		chessPieceUnicode.put("wr", new Integer[] {2, 0});
		chessPieceUnicode.put("wb", new Integer[] {3, 0});
		chessPieceUnicode.put("wn", new Integer[] {4, 0});
		chessPieceUnicode.put("wp", new Integer[] {5, 0});
		
		chessPieceUnicode.put("bk", new Integer[] {0, 62});
		chessPieceUnicode.put("bq", new Integer[] {1, 62});
		chessPieceUnicode.put("br", new Integer[] {2, 62});
		chessPieceUnicode.put("bb", new Integer[] {3, 62});
		chessPieceUnicode.put("bn", new Integer[] {4, 62});
		chessPieceUnicode.put("bp", new Integer[] {5, 62});
		
		aCurrentPlayerTimers = new Date[2];
		aPlayerTimers = new Long[] {(long) 0, (long) 0};
		
		switchTurn();
		// Make player's pieces Draggable
			makePiecesDraggable();
			
		ChessTimer.scheduleRepeating(1000);
	}

	public boolean displayGameHistory(boolean bCapture, boolean bCheck) {
		Grid moveGrid; 
		HTML moveLabel;
		HTML indexLabel;
		ChessMove chessMove;
		
		if (chessMoves != null && chessMoves.size() > 0)
		{		
			for (int i = chessMoves.size() - 1; i < chessMoves.size(); i++) {
				moveGrid = new Grid(1, 2);
				
				chessMove = (ChessMove)chessMoves.get(i);
				
				Image pieceImage = new Image("gfx/" + chessMove.PieceId + "_thumb.svg",
						0,
						0,
						45, 45);
				
				moveLabel = new HTML(
						(bCapture ? "x" : "")
						+ String.valueOf( (char) ("a".charAt(0) + chessMove.newCol))
						+ Integer.toString(chessMove.newRow + 1)
						+ (bCheck ? "+" : "")
					);
				moveLabel.setStyleName("chessmovelabel");
				
				moveGrid.setWidget(0, 0, pieceImage);
				moveGrid.setWidget(0, 1, moveLabel);

				moveGrid.setStyleName("chessboardtable");
				
				if (i%2 == 0)
				{
					indexLabel = new HTML(Integer.toString(Math.round(i/2)+1) + ".");
					indexLabel.setStyleName("chessmoveindexlabel");
					
					if (Math.round(i/2) >= gridChessMovesHistory.getRowCount())
					{
						gridChessMovesHistory.resizeRows(Math.round(i/2) + 1);
					}
						
					gridChessMovesHistory.setWidget(Math.round(i/2), 0, indexLabel);
				}
				
				gridChessMovesHistory.setWidget(Math.round(i/2), i%2 + 1, moveGrid);

				RootPanel.get("GameHistoryContainer").getElement().setScrollTop(RootPanel.get("GameHistoryContainer").getElement().getScrollHeight());
			}
		}
		else
		{
			gridChessMovesHistory = new Grid(1, 3);
			gridChessMovesHistory.setStyleName("chessmovestable");
			
			RootPanel.get("GameHistoryContainer").add(gridChessMovesHistory);
		}
		
		return true;
	}
	
	public void makePiecesDraggable() {
		bMakeDraggableRunning = true;
		
		if (nCurrentTurn == 0) {
			for (int i = 0; i < playerPieces.get(0).size(); i++) {
				try {
					chessBoardDragController.makeDraggable(
							chessSquares
								[playerPieces.get(0).get(i)[0]]
								[playerPieces.get(0).get(i)[1]].Piece);
				}
				catch (Exception e) {
				
					System.out.println("------------------- ChessBoard > makeDraggable: Exception --------------------");
					System.out.println(e.getMessage());
				}
			}
		}
		else {
			for (int i = 0; i < playerPieces.get(0).size(); i++) {			
				try {
					chessBoardDragController.makeNotDraggable(
							chessSquares
								[playerPieces.get(0).get(i)[0]]
								[playerPieces.get(0).get(i)[1]].Piece);
				}
				catch (Exception e) {
					System.out.println("------------------- ChessBoard > makeNotDraggable: Exception --------------------");
					
					System.out.println(e.getMessage());
				}
			}
		}
		
		bMakeDraggableRunning = false;
	}
	
	public void switchTurn() {
		// End Current player's turn
			Date currentTime = new Date();
			
			if (aCurrentPlayerTimers[nCurrentTurn] != null)	{
				aPlayerTimers[nCurrentTurn] += currentTime.getTime() - aCurrentPlayerTimers[nCurrentTurn].getTime();
			}
			
			// DeActivate players display
				RootPanel.get(Constants.PieceNames[nCurrentTurn] + "Display").setStyleName("playerdisplayoff");
		
		//Switch Player
		nCurrentTurn = (nCurrentTurn + 1) % 2;
		
		// Start Opponent player's turn
				
			// Start date,time for timer
				aCurrentPlayerTimers[nCurrentTurn] = new Date();
			
			// Activate players display
				RootPanel.get(Constants.PieceNames[nCurrentTurn] + "Display").setStyleName("playerdisplayon");
				
			// Get Black's Move
				if (nCurrentTurn==1) {
					Integer[] bestMove = new Integer[5];
					
					bestMove = getBestMove(nCurrentTurn, null, 0);

					if (bestMove != null && bestMove.length > 0 && bestMove[3] != null)
					{
						executeMove(nCurrentTurn, bestMove);
					}
					else
					{
						System.out.println("------------------- switchTurn > Get Black's Move: Exception --------------------");
						
						System.out.println("bestMove = null");
					}
				}
	}
	
	public void executeMove(Integer nPlayer, Integer[] move) {
		Integer row = move[0];
		Integer col = move[1];
		Integer newRow = move[2]; 
		Integer newCol = move[3];
		
		boolean bCapture = false;
		boolean bCheck = false;
		int nPlayerPiece;
		
		
		// Make player's pieces Draggable
			bMakeDraggableRunning = true;
			
			if (nPlayer == 1) {
				for (int i = 0; i < playerPieces.get(0).size(); i++) {
					try {
						chessBoardDragController.makeDraggable(
								chessSquares
									[playerPieces.get(0).get(i)[0]]
									[playerPieces.get(0).get(i)[1]].Piece);
					}
					catch (Exception e) {
						System.out.println("------------------- executeMove > makeDraggable: Exception --------------------");
						System.out.println(e.getMessage());
					}
				}
			}
			else {
				for (int i = 0; i < playerPieces.get(0).size(); i++) {				
					chessBoardDragController.makeNotDraggable(
							chessSquares
								[playerPieces.get(0).get(i)[0]]
								[playerPieces.get(0).get(i)[1]].Piece);
				}
			}
			
			bMakeDraggableRunning = false;
			
		//Update chessSquares
			//Capture
				try {
				if (chessSquares[newRow][newCol].PieceId != null)
				{
					bCapture = true;
					
					chessSquares[newRow][newCol].PieceId = null;
					RootPanel.get().remove(chessSquares[newRow][newCol].Piece);
					chessSquares[newRow][newCol].Piece.removeFromParent();
					chessSquares[newRow][newCol].Piece = null;
					
					nPlayerPiece = findPlayerPiece((nPlayer+1)%2, newRow, newCol);

					if (nPlayerPiece >= 0) {
						playerPieces.get((nPlayer+1)%2).remove(nPlayerPiece);
					}
				}
				}
				catch (Exception e) {
					System.out.println("------------------- executeMove > Update chessSquares - Capture: Exception --------------------");
					System.out.println("newRow: " + newRow);
					System.out.println("newCol: " + newCol);
					System.out.println(e.getMessage());
				}
				
			//Check
				if (execEvalFunc(
						"Check", nPlayer
						, null
						, new Integer[] {row
								, col
								, newRow
								, newCol}
						, 0) > 0)
				{
					bCheck = true;
				}
			
				
			// Update new square
				chessSquares[newRow][newCol].PieceId = chessSquares[row][col].PieceId;
				
				chessSquares
				[newRow]
				[newCol].Piece = chessSquares
				[row]
				[col].Piece;
				
				chessSquares
					[newRow]
					[newCol].Square.add(chessSquares
									[newRow]
									[newCol].Piece);
			
			// Update previous square
				chessSquares[row][col].PieceId = null;
				
		// Add move to game history
			chessMoves.add(
				new ChessMove(
						chessSquares[newRow][newCol].PieceId,
						row,
						col,
						newRow,
						newCol
				));
			
			boolean bdisplayGameHistory = displayGameHistory(bCapture, bCheck);
		
		// Update Player Pieces
			nPlayerPiece = findPlayerPiece(nPlayer, row, col);

			if (nPlayerPiece >= 0)
				playerPieces.get(nPlayer).set(
						nPlayerPiece
						, new Integer[] {newRow, newCol});
			
		// Switch Turn
			if (bdisplayGameHistory)
				switchTurn();
	}                                    
	
	public boolean isValidMove(Integer nPlayer, Integer[] pieceMove){
		return isValidMove(nPlayer, pieceMove, false);
	}
	
	public boolean isValidMove(Integer nPlayer, Integer[] pieceMove, boolean bRecursive){
		String PieceId = chessSquares[pieceMove[0]][pieceMove[1]].PieceId;
		if (PieceId != null)
			PieceId = PieceId.substring(1, 2);
		
		return isValidMove(nPlayer, pieceMove, bRecursive, PieceId);
	}
	
	public boolean isValidMove(Integer nPlayer, Integer[] pieceMove, boolean bRecursive, String PieceId){
		boolean bValid = true;
		
		
		// move on same square as other own piece
			if (chessSquares[pieceMove[2]][pieceMove[3]].PieceId != null) {
				String destPieceColor = chessSquares[pieceMove[2]][pieceMove[3]].PieceId.substring(0, 1);
				
				if (Constants.PieceColorsIndex.get(destPieceColor) == nPlayer) {
					bValid = false;
					
					System.out.println("------------------- onPreviewDrop > isValidMove FAILED--------------------");
					System.out.println("	move on same square as other own piece");
					
					return bValid;
				}
			}
			
		// move on same square as opponent's king
			if (chessSquares[pieceMove[2]][pieceMove[3]].PieceId == "k") {
				// Check isValidMove recursive call
					String destPieceColor = chessSquares[pieceMove[2]][pieceMove[3]].PieceId.substring(0, 1);
					
					if (
							(bRecursive && Constants.PieceColorsIndex.get(destPieceColor) == nPlayer)
							|| (!bRecursive)
						)
					{
						bValid = false;
						
						System.out.println("------------------- onPreviewDrop > isValidMove FAILED--------------------");
						System.out.println("	move on same square as opponent's king");
						
						return bValid;
					}
			}
			
		// pawn switches col w/o opponents piece being there
			// or it moves ahead on same square as other piece
			
			if (PieceId.equals("p")) {
				String destPieceId = chessSquares[pieceMove[2]][pieceMove[3]].PieceId;
				
				if (destPieceId != null) {
					destPieceId = destPieceId.substring(1, 2);
				
					Integer DeltaCol = Math.abs(pieceMove[1] - pieceMove[3]);
						
					if (DeltaCol == 1
							&& (destPieceId == null || destPieceId.length() == 0 || destPieceId.equals("k"))) {
							bValid = false;
	
							System.out.println("------------------- onPreviewDrop > isValidMove FAILED--------------------");
							System.out.println("	pawn switches col w/o opponents piece being there");
							
							return bValid;
						}
						else if (DeltaCol == 0 && destPieceId != null) {
							bValid = false;
							
							System.out.println("------------------- onPreviewDrop > isValidMove FAILED--------------------");
							System.out.println("	pawn moves ahead on same square as other piece");
							
							System.out.println("pieceMove[0]: " + pieceMove[0]);
							System.out.println("pieceMove[1]: " + pieceMove[1]);
							System.out.println("pieceMove[2]: " + pieceMove[2]);
							System.out.println("pieceMove[3]: " + pieceMove[3]);
							
							return bValid;
						}
				}
			}
			
		// jump over squares with pieces, except for knight
			if (!PieceId.equals("n")) {
				Integer DeltaRow = Math.abs(pieceMove[0] - pieceMove[2]);
				Integer DeltaCol = Math.abs(pieceMove[1] - pieceMove[3]);
				
				if (DeltaRow > 1 || DeltaCol > 1) {				
					Integer SwitchRow = DeltaRow > 1 ? 1 : 0; 
					Integer SwitchCol = DeltaCol > 1 ? 1 : 0;
			
					Integer DirectionRow = DeltaRow > 1 ? (pieceMove[0] - pieceMove[2]) / Math.abs(pieceMove[0] - pieceMove[2]) : 0; 
					Integer DirectionCol = DeltaCol > 1 ? (pieceMove[1] - pieceMove[3]) / Math.abs(pieceMove[1] - pieceMove[3]) : 0;
					
					String currentPieceId = null;
					
					for (int i = 1; i < (DeltaRow > 1 ? DeltaRow : DeltaCol); i++) {
						try {
							currentPieceId = chessSquares
									[pieceMove[2] + SwitchRow * DirectionRow * i]
									[pieceMove[3] + SwitchCol * DirectionCol * i].PieceId;
						}
						catch (Exception e) {
						
							System.out.println("------------------- jump over squares with pieces, except for knight --------------------");
							System.out.println("pieceMove[0]: " + pieceMove[0]);
							System.out.println("pieceMove[1]: " + pieceMove[1]);
							System.out.println("pieceMove[2]: " + pieceMove[2]);
							System.out.println("pieceMove[3]: " + pieceMove[3]);
							
							System.out.println("pieceMove[2] + SwitchRow * DirectionRow * i: " + Integer.toString(pieceMove[2] + SwitchRow * DirectionRow * i));
							System.out.println("pieceMove[3] + SwitchCol * DirectionCol * i: " + Integer.toString(pieceMove[3] + SwitchCol * DirectionCol * i));
							System.out.println(e.getMessage());
						}
						 
						
						if (currentPieceId != null) {
							bValid = false;
							
							System.out.println("------------------- onPreviewDrop > isValidMove FAILED--------------------");
							System.out.println("	jump over squares with pieces, except for knight");
							
							return bValid;
						}
					}
				}
			}
			
		// king moves into check
			if (PieceId.equals("k")) {
				int opponentPlayer = (nPlayer+1)%2;
				String opponentColor = Constants.PieceColors[opponentPlayer];
				ArrayList<Integer[]> pieceMoves;
				String opponentPiece;
				
				// Opponent's Pawns - p
					int pawnDirection = nPlayer == 0 ? 1 : -1;
					opponentPiece = opponentColor + "p";

					if (
							(pieceMove[2] + pawnDirection >= 0 && pieceMove[2] + pawnDirection <= 7)
							&&
							((pieceMove[3] < 7
								&& opponentPiece.equals(chessSquares
										[pieceMove[2] + pawnDirection]
											[pieceMove[3] + 1].PieceId))
							||
							(pieceMove[3] > 0
								&& opponentPiece.equals(chessSquares
										[pieceMove[2] + pawnDirection]
											[pieceMove[3] - 1].PieceId))
							)
						) {
						bValid = false;
						
						System.out.println("------------------- onPreviewDrop > isValidMove FAILED--------------------");
						System.out.println("	king moves into pawn's check");
						
						return bValid;
					}

				// Opponent's Knights - n
				// , Opponent's Bishops - b
				// , Opponent's Rook - r
				// , Opponent's Queen - q
					String[] opponentPieces = new String[]{"n", "b", "r", "q"};

					
					// get all possible moves for an opponent's piece from the king's square
						for (int i = 0; i < opponentPieces.length; i++) {
							opponentPiece = opponentColor + opponentPieces[i];
							
							pieceMoves = Constants.getAllPieceMoves(opponentPiece, pieceMove[2], pieceMove[3]);
							
							for (int j = 0; j < pieceMoves.size(); j++) {
								
								// check for the piece
									if (opponentPiece.equals(chessSquares
											[pieceMoves.get(j)[2]]
											[pieceMoves.get(j)[3]].PieceId)){
										if (isValidMove(opponentPlayer
															, new Integer[] {
																pieceMoves.get(j)[2], pieceMoves.get(j)[3]  
																, pieceMoves.get(j)[0], pieceMoves.get(j)[1]
																}
															, true)
										) {
											bValid = false;
											
											System.out.println("------------------- onPreviewDrop > isValidMove FAILED--------------------");
											System.out.println("	king moves into opponent's piece check");
											
											return bValid;
										}
									}
							}
						}
			}
			
		// king moves 1 square away from other king
		
		return bValid;
	}

	public Integer[] getBestMove(Integer nPlayer, HashMap<String, Integer[]> previousMoves, Integer nCurrentTreeDepth) {
		Integer[] bestMove = new Integer[5];
		ArrayList<Integer[]> pieceMoves;
		Integer[] currentMove;
		

		if (previousMoves == null) {
			previousMoves = new HashMap<String, Integer[]>();
		}
		
		for (int i = 0; i < playerPieces.get(nPlayer).size(); i++) {
			pieceMoves = getBestPieceMoves(nPlayer, previousMoves, playerPieces.get(nPlayer).get(i), nCurrentTreeDepth);
		
			for (int j = 0; j < pieceMoves.size(); j++) {
				currentMove = pieceMoves.get(j);
				
				if (Math.round(nCurrentTreeDepth / 2) < Constants.chessAITreeDepth) {
					HashMap<String, Integer[]> newPreviousMoves;
					newPreviousMoves = previousMoves;
					
					newPreviousMoves.put(currentMove[1].toString() + currentMove[2].toString(), null);
					newPreviousMoves.put(currentMove[3].toString() + currentMove[4].toString(), playerPieces.get(nPlayer).get(i));
					
					bestMove = getBestMove((nPlayer+1)%2, newPreviousMoves, nCurrentTreeDepth + 1);
				}
				else if (bestMove[4]==null || bestMove[4] < currentMove[4]) {
					System.arraycopy(currentMove, 0, bestMove, 0, currentMove.length);
				}
			}
		}
		
		return bestMove;

	}
	
	/**
	 * @param Integer nPlayer
	 * @param HashMap(String, Integer[]) previousMoves
	 * @param Integer[] currentPiece
	 * @param Integer nCurrentTreeDepth
	 * @return ArrayList(Integer[]): [row, col, newRow, newCol, EvalScore]
	 */
	public ArrayList<Integer[]> getBestPieceMoves(Integer nPlayer, HashMap<String, Integer[]> previousMoves, Integer[] currentPiece, Integer nCurrentTreeDepth) {
		ArrayList<Integer[]> pieceMoves;
		Integer[] pieceMoveScore;
		ArrayList<Integer[]> bestPieceMoves = new ArrayList<Integer[]>();
		Integer bestScore = -1000;
		HashMap<String, Integer> evalFuncScores = new HashMap<String, Integer>();

		
		// get all possible moves for the piece
		pieceMoves = Constants.getAllPieceMoves(chessSquares[currentPiece[0]][currentPiece[1]].PieceId, currentPiece[0], currentPiece[1]);
		
		for (int i = 0; i < pieceMoves.size(); i++) {
			// exclude impossible moves
				if (!isValidMove(nPlayer, pieceMoves.get(i)))
					continue;

			// Calculate score
				Integer score = 0;
			
				for (String key : Constants.chessAIEvalFuncWeights.keySet()) {
					if (Constants.chessAIEvalFuncWeights.get(key) > 0) {
						evalFuncScores.put(
								key
								, (int) (
											(((double)execEvalFunc(
											key, nPlayer
											, previousMoves
											, pieceMoves.get(i)
											, nCurrentTreeDepth)) / 100)
										* Constants.chessAIEvalFuncWeights.get(key)
								));
						
						score += evalFuncScores.get(key);
					}
					else {
						evalFuncScores.put(key, null);
					}
				}
					
			// Store score
				if (score > bestScore)
				{					
					pieceMoveScore = new Integer[5];
					System.arraycopy(pieceMoves.get(i), 0, pieceMoveScore, 0, 4);
					pieceMoveScore[4] = score;

if (score > 0) {
System.out.println("------------------- getBestPieceMoves --------------------");
System.out.println("	score: " + score);
for (String key : Constants.chessAIEvalFuncWeights.keySet()) {
	if (evalFuncScores.get(key) > 0) {
		System.out.println("	" + key + ": " + evalFuncScores.get(key));
	}
}
}
					
					bestPieceMoves.add(pieceMoveScore);
					bestScore = score; 
				}
		}
		
		return bestPieceMoves;
	}
	
	/**
	 * @param Integer nPlayer
	 * @param HashMap(String, Integer[]) previousMoves
	 * @param Integer[] currentPiece
	 * @param Integer nCurrentTreeDepth
	 * @return Integer score: value 0 to 100
	 */
	public Integer execEvalFunc(String evalFunc, Integer nPlayer, HashMap<String, Integer[]> previousMoves, Integer[] currentPieceMove, Integer nCurrentTreeDepth) {
		Integer score = 0;
		int row = currentPieceMove[0];
		int col = currentPieceMove[1];
		int newRow = currentPieceMove[2];
		int newCol = currentPieceMove[3];
		
		int currentRow = 0;
		int currentCol = 0;

		// CheckMate
			if (evalFunc == "CheckMate" || evalFunc == "Check") {
				// find opponent's king
					Integer kingRow = null, kingCol = null;

					
					for (int i = 0; i < playerPieces.get((nPlayer+1)%2).size(); i++) {
						currentRow = playerPieces.get((nPlayer+1)%2).get(i)[0];
						currentCol = playerPieces.get((nPlayer+1)%2).get(i)[1];
						
						if(chessSquares
								[currentRow]
								[currentCol].PieceId.substring(1, 2).equals("k")) {
							kingRow = currentRow;
							kingCol = playerPieces.get((nPlayer+1)%2).get(i)[1];
							
							break;
						}
					}
				
				// check Check
					if (kingRow != null && kingCol != null) {
						String PieceId = chessSquares[row][col].PieceId;
						
						Integer DeltaRow = Math.abs(newRow - kingRow);
						Integer DeltaCol = Math.abs(newCol - kingCol);


						if (Constants.isLegalMove(PieceId
												, DeltaRow, DeltaCol
												, newRow, newCol
												, Constants.PieceColors[(nPlayer+1)%2] + "k")
							&& isValidMove(nPlayer
											, new Integer[] {
												newRow, newCol  
												, kingRow, kingCol
												}
											, true
											, PieceId.substring(1, 2))) {
							if (evalFunc == "Check") {
								score = 100;	
							}
							else {
								
							}
						}
					}
			}
		
		// SumPieceWeights
			else if (evalFunc == "SumPieceWeights") {
				// score += Current Player pieces 
					for (int i = 0; i < playerPieces.get(nPlayer).size(); i++) {
						currentRow = playerPieces.get(nPlayer).get(i)[0];
						currentCol = playerPieces.get(nPlayer).get(i)[1];
						
						score += Constants.PieceValues.get(
									chessSquares
										[currentRow]
										[currentCol].PieceId.substring(1, 2));
					}

				// score -= Opponent Player pieces 
					for (int i = 0; i < playerPieces.get((nPlayer+1)%2).size(); i++) {
						currentRow = playerPieces.get((nPlayer+1)%2).get(i)[0];
						currentCol = playerPieces.get((nPlayer+1)%2).get(i)[1];
						
						if (!(currentRow == newRow && currentCol == newCol)) {
							score -= Constants.PieceValues.get(
										chessSquares
											[currentRow]
											[currentCol].PieceId.substring(1, 2));
						}
					}

				score = (int) ((score.doubleValue() / 39) * 100);
			}
			
		// ControlCenter
			else if (evalFunc == "ControlCenter") {
				score = 0;
			}
			
		// CoverPieces
			else if (evalFunc == "CoverPieces") {
				score = 0;
			}
			
		// NoDoublePawn
			else if (evalFunc == "NoDoublePawn") {
				score = 0;
			}
			
		
		return score;
	}
}